package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.market.CouponApi;
import com.jzo2o.api.market.dto.request.CouponUseReqDTO;
import com.jzo2o.api.market.dto.response.AvailableCouponsResDTO;
import com.jzo2o.api.market.dto.response.CouponUseResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersCreateService;
import com.jzo2o.redis.annotations.Lock;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

/**
 * <p>
 * 下单服务类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersCreateServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersCreateService {

    @Autowired
    private ServeApi serveApi;
    @Autowired
    private AddressBookApi addressBookApi;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IOrdersCreateService owner;
    @Autowired
    private NativePayApi nativePayApi;
    @Autowired
    private TradingApi tradingApi;
    @Autowired
    private TradeProperties tradeProperties;
    @Autowired
    private CouponApi couponApi;

    /**
     * 获取可用优惠券
     *
     * @param serveId 服务项目id
     * @param purNum  购买数量
     * @return 可用优惠券
     */
    @Override
    public List<AvailableCouponsResDTO> getAvailableCoupons(Long serveId, Integer purNum) {
        //根据服务id 获取服务单价
        ServeAggregationResDTO serve = serveApi.findById(serveId);
        if (ObjectUtil.isNull(serve) || serve.getSaleStatus() != 2){
            throw new ForbiddenOperationException("服务异常");
        }
        //计算需要付的总金额
        BigDecimal totalAmount = serve.getPrice().multiply(new BigDecimal(purNum));
        return couponApi.getAvailable(totalAmount);
    }

    /**
     * 请求支付服务查询支付结果
     *
     * @param id
     * @return
     */
    @Override
    public OrdersPayResDTO getPayResultFromTradServer(Long id) {
        //1. 查询订单 判断订单状态是否为待支付，如果是，直接返回错误提示
        Orders orders = baseMapper.selectById(id);
        if (ObjectUtil.isEmpty(orders)){
            throw new ForbiddenOperationException("订单不存在");
        }
        //2. 判断订单状态是否为待支付，和是否有支付服务交易订单号 都满足才远程调用支付微服务
        if (orders.getPayStatus()==2 && ObjectUtil.isNotEmpty(orders.getTradingOrderNo())){
            //调用支付微服务查询支付结果
            TradingResDTO resDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            //解析支付结果 获取支付状态
            TradingStateEnum tradingState = resDTO.getTradingState();
            //根据支付状态判断 来更新订单状态
            boolean update = this.lambdaUpdate()
                    //交易状态: 4-已结算  订单状态:派单中
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.YJS), Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                    //交易状态: 3-付款失败  订单状态:已关闭
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.FKSB), Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                    //交易状态: 5-取消订单  订单状态:已取消
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.QXDD), Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                    //交易状态: 4-已结算  支付状态:支付成功
                    .set(ObjectUtil.equal(tradingState, TradingStateEnum.YJS), Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                    //第三方支付交易单号
                    .set(ObjectUtil.isNotEmpty(resDTO.getTransactionId()), Orders::getTransactionId, resDTO.getTransactionId())
                    .set(Orders::getPayTime, LocalDateTime.now())
                    //根据订单id更新
                    .eq(Orders::getId, id)
                    .update();
            if (!update){
                log.info("更新：{}订单失败",orders.getId());
                throw new ForbiddenOperationException("更新订单"+orders.getId()+"状态失败");
            }
        }

        //返回结果
        Orders newOrders = baseMapper.selectById(id);
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        //给返回值业务系统订单号 赋值
        ordersPayResDTO.setProductOrderNo(newOrders.getId());
        //给返回值交易系统订单号 赋值
        ordersPayResDTO.setTradingOrderNo(newOrders.getTradingOrderNo());
        //给返回值支付渠道 赋值
        ordersPayResDTO.setTradingChannel(newOrders.getTradingChannel());
        //给返回值支付状态 赋值
        ordersPayResDTO.setPayStatus(newOrders.getPayStatus());

        return ordersPayResDTO;
    }

    /**
     * 订单支付
     *
     * @param id              订单id
     * @param ordersPayReqDTO 支付请求对象
     * @return 支付响应
     */
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1. 根据订单号查询订单是否存在，如果不存在，直接返回错误提示
        Orders orders = baseMapper.selectById(id);
        if (ObjectUtil.isNull(orders)){
            throw new ForbiddenOperationException("订单不存在");
        }
        //2. 判断订单是否已完成支付，如果已完成支付，直接返回错误提示
        if (orders.getPayStatus() == 4 && StringUtils.isNotEmpty(orders.getTransactionId())) {
            throw new ForbiddenOperationException("订单已经支付了");
        }
        //3. 使用nativePayApi远程调用创建支付记录，获取支付二维码地址，返回前端
        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //业务系统标识
        nativePayReqDTO.setProductAppId("jzo2o.orders");
        //业务系统订单号
        nativePayReqDTO.setProductOrderNo(id);
        //支付渠道
        nativePayReqDTO.setTradingChannel(ordersPayReqDTO.getTradingChannel());
        //支付金额
        nativePayReqDTO.setTradingAmount(orders.getRealPayAmount());
        //备注
        nativePayReqDTO.setMemo(orders.getServeItemName());
        //判断支付渠道
        if(ObjectUtil.equal(ordersPayReqDTO.getTradingChannel(), PayChannelEnum.ALI_PAY)){
            //支付宝商户ID
            nativePayReqDTO.setEnterpriseId(tradeProperties.getAliEnterpriseId());
        }
        if(ObjectUtil.equal(ordersPayReqDTO.getTradingChannel(), PayChannelEnum.WECHAT_PAY)){
            //微信商户ID
            nativePayReqDTO.setEnterpriseId(tradeProperties.getWechatEnterpriseId());
        }
        //判断交易渠道是否切换
        if(ObjectUtil.isNotEmpty(orders) &&
        ! StringUtils.equals(orders.getTradingChannel(),ordersPayReqDTO.getTradingChannel().toString())
        ){
            nativePayReqDTO.setChangeChannel(true);
        }else {
            nativePayReqDTO.setChangeChannel(false);
        }
        //调用支付微服务
        NativePayResDTO resDTO = nativePayApi.createDownLineTrading(nativePayReqDTO);
        //4. 更新订单中的部分字段信息（支付服务交易订单号、支付渠道）
        orders.setTradingOrderNo(resDTO.getTradingOrderNo());
        orders.setTradingChannel(resDTO.getTradingChannel());
        baseMapper.updateById(orders);
        //5. 封装返回结果
        OrdersPayResDTO ordersPayResDTO = BeanUtil.copyProperties(resDTO, OrdersPayResDTO.class);
        //设置支付状态 待支付
        ordersPayResDTO.setPayStatus(2);
        return ordersPayResDTO;
    }

    /**
     * 创建订单
     *
     * @param placeOrderReqDTO 下单请求参数
     * @return 下单结果
     */
    @Override
    public PlaceOrderResDTO placeOrder(PlaceOrderReqDTO placeOrderReqDTO) {
       return owner.placeOrder(UserContext.currentUserId(),placeOrderReqDTO);
    }
    @Lock(formatter = "ORDERS:CREATE:LOCK:#{userId}:#{placeOrderReqDTO.serveId}",waitTime = 1,time = 30,unlock = false)
    public PlaceOrderResDTO placeOrder(Long userId,PlaceOrderReqDTO placeOrderReqDTO){
        //feign远程调用基础服务和地址服务
        ServeAggregationResDTO serveDTO = serveApi.findById(placeOrderReqDTO.getServeId());
        if (ObjectUtil.isNull(serveDTO) || serveDTO.getSaleStatus()!=2){
            throw new ForbiddenOperationException("服务不存在或未启用");
        }

        AddressBookResDTO addressDTO = addressBookApi.detail(placeOrderReqDTO.getAddressBookId());
        if (ObjectUtil.isNull(addressDTO)){
            throw new ForbiddenOperationException("地址不存在");
        }

        //copy属性
        Orders orders = new Orders();
        BeanUtil.copyProperties(serveDTO, orders);

        //补全属性
        //地址
        //服务详细地址
        orders.setServeAddress(addressDTO.getAddress());
        //联系人手机号
        orders.setContactsPhone(addressDTO.getPhone());
        //联系人名字
        orders.setContactsName(addressDTO.getName());
        //经度
        orders.setLon(addressDTO.getLon());
        //纬度
        orders.setLat(addressDTO.getLat());

        //订单id
        orders.setId(generateOrderId());
        //订单所属人id
        orders.setUserId(UserContext.currentUserId());
        //服务id
        orders.setServeId(placeOrderReqDTO.getServeId());
        //订单状态
        orders.setOrdersStatus(0);
        //支付状态
        orders.setPayStatus(2);
        //购买数量
        orders.setPurNum(placeOrderReqDTO.getPurNum());
        //订单总金额 = 单价 * 数量
        orders.setTotalAmount(serveDTO.getPrice().multiply(new BigDecimal(placeOrderReqDTO.getPurNum())));
        //优惠金额
        orders.setDiscountAmount(new BigDecimal(0));
        //实际支付金额 = 订单总金额 - 优惠金额
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));
        //服务开始时间
        orders.setServeStartTime(placeOrderReqDTO.getServeStartTime());
        //用户端是否展示
        orders.setDisplay(1);
        //排序字段
        orders.setSortBy(DateUtils.toEpochMilli(placeOrderReqDTO.getServeStartTime()) + orders.getId() % 100000);

        //保存
       if (ObjectUtil.isNull( orders.getDiscountAmount())){
           //未使用优惠券
           owner.saveOrders(orders);
       }else {
           //使用优惠券
           owner.saveOrUpdatesByCoupon(orders,placeOrderReqDTO);
       }

        //返回结果
        return new PlaceOrderResDTO(orders.getId());
    }

   @Transactional
   public void saveOrders(Orders orders) {
        //不使用优惠券
        this.save(orders);
    }

    /**
     * 创建订单 使用优惠券
     *
     * @param orders
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void saveOrUpdatesByCoupon(Orders orders,PlaceOrderReqDTO placeOrderReqDTO) {
        /**
         * 1. 创建订单（前面已经做了）
         * 2. 调用优惠券微服务核销优惠券
         * 3. 修改订单的优惠金额和实付金额
         */
        CouponUseReqDTO couponUseReqDTO =new CouponUseReqDTO();
        //优惠券id
        couponUseReqDTO.setId(placeOrderReqDTO.getCouponId());
        //订单id
        couponUseReqDTO.setOrdersId(orders.getId());
        //订单总金额
        couponUseReqDTO.setTotalAmount(orders.getTotalAmount());

        //调用优惠券微服务核销优惠券
        CouponUseResDTO use = couponApi.use(couponUseReqDTO);

        //修改订单的优惠金额和实付金额
        //优惠金额
        orders.setDiscountAmount(use.getDiscountAmount());
        //实际支付金额 = 订单总金额 - 优惠金额
        orders.setRealPayAmount(orders.getTotalAmount().subtract(orders.getDiscountAmount()));
        this.save(orders);
    }


    //TODO 订单id生成
    private Long generateOrderId() {
        //订单id 19位：2位年+2位月+2位日+13位序号(自增)
        //先使用工具类按照年月日生成2位年+2位月+2位日
        Long yyMMdd = DateUtils.getFormatDate(LocalDateTime.now(), "yyMMdd");

        //生成自增序号 使用Redis
        Long num = redisTemplate.opsForValue().increment(RedisConstants.Lock.ORDERS_SHARD_KEY_ID_GENERATOR, 1);//1 代表的是每次增长量为1

        //组合订单id
        return yyMMdd * 10000000000000L + num;
    }
}
